package yahoodata;

import java.io.*;
import java.text.*;
import java.util.*;
import java.util.concurrent.*;

/** Because this package is so tightly bound to http://finance.yahoo.com, it did not make
 *  sense to make these classes modular and re-usable. This is just a simple utility, and
 *  nothing more.
 */
public class Main {
    
    public  static enum                ProductType { stock, etf, index, _null }
    private static Map<String, String> reservedFileNames = new HashMap<String, String>(3);
    
    public Main() { }
    
    public static void main(String[] args) {
        System.out.println(); System.out.println(); System.out.println();
        System.out.println("Starting stock/etf/index data download tool for Yahoo Finance...");
        System.out.println("Detailed documentation can be found here: http://www.dwilkins.net/Dev/DataBot.aspx");
        System.out.println("[enter \"q\" at anytime to exit.");
        System.out.println(); System.out.println("==========================");
        
        BufferedReader shellRdr = new BufferedReader(new InputStreamReader(System.in));
        
        Date        today       = new Date(System.currentTimeMillis());
        boolean     finalOK     = false;
        int         numbWorkers = 5;
        int         numbSymbols = 0;
        ProductType productType = ProductType._null;
        
        String symbInputFileName      = "_null";
        String eventsOutputFileName   = "_null";
        String profilesOutputFileName = "_null";
        String errorsOutputFileName   = "_null";
        
        String eventsStartDateString = "_null"; Date eventsStartDate = null;
        String eventsEndDateString   = "_null"; Date eventsEndDate   = null;
        
        String profilesStartDateString = "_null";  Date profilesStartDate = null;
        String profilesEndDateString   = "_null";  Date profilesEndDate   = null;
        
        Calendar startCal = GregorianCalendar.getInstance();
        Calendar endCal   = GregorianCalendar.getInstance();
        
        while(!finalOK) {
            numbWorkers = 5;
            numbSymbols = 0;
            productType = ProductType._null;
     
            symbInputFileName      = "_null";    
            eventsOutputFileName   = "_null";
            profilesOutputFileName = "_null";
            errorsOutputFileName   = "_null";
            
            eventsStartDateString = "_null"; eventsStartDate = null;
            eventsEndDateString   = "_null"; eventsEndDate   = null;
            
            profilesStartDateString = "_null"; profilesStartDate = null;
            profilesEndDateString   = "_null"; profilesEndDate   = null;
            
            while(true) {  // __product type__
                try {
                    System.out.println("product type in the input file [\"stock\", \"index\", \"etf\"]");
                    String s = shellRdr.readLine();
                    if(s.equals("q") || s.equals("Q")) { System.exit(1); }
                    productType = ProductType.valueOf(s);
                    break;
                } catch(IOException e) { System.err.println("__ERROR__: I/O exception."); }
                  catch(IllegalArgumentException e) {
                    System.err.println("__ERROR__: the supported product types are: [\"stock\", \"index\", \"etf\"].");
                  }
            } // __product type__
           
            
            while(true) {  // __symb input file name__
                try {
                    System.out.println("path of the _input_ file. ex: \"/tmp/sp500.txt\" --> ");
                    symbInputFileName = shellRdr.readLine();
                    if(symbInputFileName.equals("q") || symbInputFileName.equals("Q")) { System.exit(1); }
                    if(Main.validateReadableFile(symbInputFileName)) {
                        numbSymbols = Main.getNumbOfLinesInFile(symbInputFileName);
                        if(numbSymbols < 0) {
                            System.err.println("__ERROR__: cannot read from file.");
                            continue;
                        }
                        Main.reservedFileNames.put(symbInputFileName, "readable symbols file name.");
                        break;
                    }
                } catch(IOException e) { System.err.println("__ERROR__: I/O exception."); }
            } // __symb input file name__

            
            while(true) {  // __events file output name__
                try {
                    System.out.println("path of the file to which to _save_ events info. ex: \"/tmp/sp500events.txt\" --> ");
                    eventsOutputFileName = shellRdr.readLine();
                    if(eventsOutputFileName.equals("q") || eventsOutputFileName.equals("Q")) { System.exit(1); }
                    if(Main.validateWritableFile(eventsOutputFileName)) {
                        if(Main.reservedFileNames.keySet().contains((String) eventsOutputFileName)) {
                            String mssg = (String) Main.reservedFileNames.get(eventsOutputFileName);
                            System.err.println("__ERROR__: \"" + eventsOutputFileName + "\" already used as the " + mssg);
                            continue;
                        }
                        Main.reservedFileNames.put(eventsOutputFileName, "writable events file name.");
                        break;
                    }
                } catch(IOException e) { System.err.println("__ERROR__: I/O exception."); }
            }  // __events file output name__
            

            while(true) {  // __profiles file output name__
                try {
                    System.out.println("path of the file to which to _save_ profiles info. ex: \"/tmp/sp500profiles.txt\" --> ");
                    profilesOutputFileName = shellRdr.readLine();
                    if(profilesOutputFileName.equals("q") || profilesOutputFileName.equals("Q")) { System.exit(1); }
                    if(Main.validateWritableFile(profilesOutputFileName)) {
                        if(Main.reservedFileNames.keySet().contains((String) profilesOutputFileName)) {
                            String mssg = (String) Main.reservedFileNames.get(profilesOutputFileName);
                            System.err.println("__ERROR__: \"" + profilesOutputFileName + "\" already used as the " + mssg);
                            continue;
                        }
                        Main.reservedFileNames.put(profilesOutputFileName, "writable profiles file name.");
                        break;
                    }
                } catch(IOException e) { System.err.println("__ERROR__: I/O exception."); }
            }  // __profiles file output name__
            
            
            while(true) {  // __errors file output name__
                try {
                    System.out.println("path of the file to which to _save_ invalid symbols / errors. ex: \"/tmp/sp500errors.txt\" --> ");
                    errorsOutputFileName = shellRdr.readLine();
                    if(errorsOutputFileName.equals("q") || errorsOutputFileName.equals("Q")) { System.exit(1); }
                    if(Main.validateWritableFile(errorsOutputFileName)) {
                        if(Main.reservedFileNames.keySet().contains((String) errorsOutputFileName)) {
                            String mssg = (String) Main.reservedFileNames.get(errorsOutputFileName);
                            System.err.println("__ERROR__: \"" + errorsOutputFileName + "\" already use as the " + mssg);
                            continue;
                        }
                        Main.reservedFileNames.put(errorsOutputFileName, "writable errors file name.");
                        break;
                    }
                } catch(IOException e) { System.err.println("__ERROR__: I/O exception."); }
            }  // __errors file output name__
            
            
            SimpleDateFormat dateConverter = new SimpleDateFormat("MM-dd-yyyy");
            
            while(true) {  // __events: start/end dates__
                String startDateString = "_null";  Date startDate = null;
                String endDateString   = "_null";  Date endDate   = null;
                
                while(true) {  // _events: start date_
                    try {
                        System.out.println("download _events_ data starting on this date [mm-dd-yyyy]: ");
                        startDateString = shellRdr.readLine();
                        if(startDateString.equals("q") || startDateString.equals("Q")) { System.exit(1); }
                        if(Main.validateDateFormat(startDateString)) {
                            startDate = dateConverter.parse(startDateString);
                            break;
                        }
                    } catch(IOException    e) { System.err.println("__ERROR__: I/O exception."); }
                      catch(ParseException e) {
                          System.err.println("__ERROR__: invalid data format: \"" + startDateString + "\".");
                      } 
                } // _events: start date_
                
                if(startDate.after(today)) {
                    System.err.println("__ERROR__: the start date is after today. Do not know the future.");
                    continue;
                }
                
                while(true) {  // _events: end date_
                    try {
                        endDateString = dateConverter.format(today);
                        System.out.println("download event data ending on this date [mm-dd-yyyy] (default = \"" + endDateString + "\"): ");
                        endDateString = shellRdr.readLine();
                        if(endDateString.equals("q") || endDateString.equals("Q")) { System.exit(1); }
                        if(endDateString.length() == 0) { endDateString = dateConverter.format(today); }
                        if(Main.validateDateFormat(endDateString)) {
                            endDate = dateConverter.parse(endDateString);
                            break;
                        }
                    } catch(ParseException e) {
                        System.err.println("__ERROR__: invalid date format: \"" + endDateString + "\".");
                    } catch(IOException e) { System.err.println("__ERROR__: I/O exception."); }
                } // _events: end date_
                
                if(endDate.after(today)) {
                    System.err.println("__ERROR__: the end date is after today. Do not know the future.");
                    continue;
                }
                if(endDate.before(startDate)) {
                    System.err.println("__ERROR__: the end date is before the start date.");
                    continue;
                }
                
                eventsStartDateString = startDateString;  eventsEndDateString = endDateString;
                eventsStartDate       = startDate;        eventsEndDate       = endDate;
                break;
            } // __events: (start / end) dates__
            
            
            while(true) {  // __profiles: (start / end) dates__
                String startDateString = "_null";  Date startDate = null;
                String endDateString   = "_null";  Date endDate   = null;
                
                while(true) {  // _profiles: start date_
                    try {
                        startDateString = eventsStartDateString;
                        System.out.println("build a profile using event data starting on this date [mm-dd-yyyy] (default = \"" + startDateString + "\"): ");
                        startDateString = shellRdr.readLine();
                        if(startDateString.length() == 0) { startDateString = eventsStartDateString; }
                        if(startDateString.equals("q") || startDateString.equals("Q")) { System.exit(1); }
                        if(Main.validateDateFormat(startDateString)) {
                            startDate = dateConverter.parse(startDateString);
                            break;
                        }
                    } catch(IOException e) { System.err.println("__ERROR__: I/O exception."); }
                      catch(ParseException e) {
                        System.err.println("__ERROR__: invalid data format: \"" + startDateString + "\".");
                      } 
                }  //  _profiles: start date_
                
                if(startDate.after(today)) {
                    System.err.println("__ERROR__: the start date is after today. Do not know the future.");
                    continue;
                }
                
                if(startDate.before(eventsStartDate)) {
                    System.out.println("_____NOTE_NOTE____:");
                    System.out.println("The earliest date for building the profile: \"" + startDateString + "\" is before the");
                    System.out.println("earliest date for the downloading of events: \"" + eventsStartDateString + "\".");
                    System.out.println("Since building a profile requires downloading all the events in the profile date range anyway,");
                    System.out.println("the events starting on \"" + startDateString + "\" will be downloaded.");
                    eventsStartDateString = startDateString; eventsStartDate = startDate;
                }
                
                while(true) {  // _profiles: end date_
                    try {
                        endDateString = dateConverter.format(today);
                        System.out.println("build a profile using event data ending on this date [mm-dd-yyyy] (default = \"" + endDateString + "\"): ");
                        endDateString = shellRdr.readLine();
                        if(endDateString.equals("q") || endDateString.equals("Q")) { System.exit(1); }
                        if(endDateString.length() == 0) { endDateString = dateConverter.format(today); }
                        if(Main.validateDateFormat(endDateString)) { endDate = dateConverter.parse(endDateString); break; }
                    } catch(IOException    e) { System.err.println("__ERROR__: I/O exception."); }
                      catch(ParseException e) {
                        System.err.println("__ERROR__: invalid date format: \"" + endDateString + "\".");
                      } 
                }  // _profiles: end date_
                
                if(endDate.after(today)) {
                    System.err.println("__ERROR__: the end date is after today. Do not know the future.");
                    continue;
                }
                if(endDate.before(startDate)) {
                    System.err.println("__ERROR__: the end date is before the start date.");
                    continue;
                }
                
                if(endDate.after(eventsEndDate)) {
                    System.out.println("_____NOTE_NOTE____:");
                    System.out.println("The latest date for building the profile: \"" + endDateString + "\" is after the");
                    System.out.println("latest date for downloading events data: \"" + eventsEndDateString + "\". Since building a");
                    System.out.println("profile requires downloading all the events in the profile date range, events ending on");
                    System.out.println("\"" + endDateString + "\" will be downloaded.");
                    eventsEndDateString = endDateString;  eventsEndDate = endDate;
                }
                profilesStartDateString = startDateString; profilesStartDate = startDate;
                profilesEndDateString   = endDateString;   profilesEndDate   = endDate;
                break;   
            } // __profiles: (start / end) date__

                        
            startCal.setTime(eventsStartDate);
            endCal.setTime(eventsEndDate);
            
            // the next block of code _estimates_ the number of trade days in the time series.
            // the exchange is not open on weekends.
            // and, i roughly guess that, when averaged over 12 months, the USA has one exchange
            // holiday each month such as for: Labor Day, Thanksgiving, Fourth of July, etc.
            int dayCount = 0;  int weekendCount = 0;  int holidayCount = 0;
            while(startCal.getTime().compareTo(endCal.getTime()) != 0) {
                startCal.add(Calendar.DATE, 1);
                dayCount++;
                if(startCal.get(Calendar.DAY_OF_WEEK)  == Calendar.SATURDAY) { weekendCount++; }
                if(startCal.get(Calendar.DAY_OF_MONTH) == 1)                 { holidayCount++;  }
            }
            dayCount = (dayCount - (weekendCount * 2) - holidayCount);

            while(true) {  // __worker threads__
                try {
                    System.out.println();
                    System.out.println("---Worker Threads--- [how many?]");
                    System.out.println("Increasing the number of workers might speeds-up the download, but at the expense of consuming memory.");
                    System.out.println("The number of symbols to download = " + numbSymbols + ". Each worker is assigned one symbol at a time.");
                    System.out.println("One symbol will have about " + dayCount + " events to download. A worker writes the events to disk, discards");
                    System.out.println("the events to reclaim memory, then starts downloading events for another symbol.");
                    System.out.println("The greater the number of \"events\" for each symbol, the more memory each worker consumes.");
                    System.out.println("The greater the number of workers, the more memory is consumed.");
                    System.out.println("How many workers?");
                    System.out.println("[hit the \"enter\" key for a default of 5 workers]: ");
                    String s = shellRdr.readLine();
                    
                    if(s.equals("q") || s.equals("Q")) { System.exit(1); }
                    if(s.length() == 0) { numbWorkers = 5; break; }
                    try {
                        numbWorkers = Integer.valueOf(s).intValue();
                        if(numbWorkers < 1) { System.err.println("__ERROR__: enter a positive integer."); continue; }
                        if(numbWorkers > numbSymbols) {
                            System.out.println("_____NOTE_NOTE____:");
                            System.out.println("There is no benefit to having more workers than symbols.");
                            System.out.println("A worker's unit of work is a \"symbol\". Adjusting number of workers to number of symbols.");
                            System.out.println("Number of workers = " + numbSymbols);
                            numbWorkers = numbSymbols;
                        }
                        break;
                    } catch(NumberFormatException e) {  System.err.println("__ERROR_: enter an integer, or the return key."); }
                } catch(IOException e) {  System.err.println("__ERROR__: I/O exception.");   }
            }  // __worker threads__
        
 
            while(true) {
                try {
                    System.out.println("---------- summary ----------");
                    System.out.println("number of workers:  " + numbWorkers);
                    System.out.println("events to download: " + ((int) (dayCount * numbSymbols)));
                    System.out.println("product type:       " + productType);
                    System.out.println("events   start  date: " + eventsStartDateString);
                    System.out.println("events   end    date: " + eventsEndDateString);
                    System.out.println("profiles start  date: " + profilesStartDateString);
                    System.out.println("profiles end    date: " + profilesEndDateString);
                    System.out.println("data     input  file: \"" + symbInputFileName   +"\"");
                    System.out.println("profile  output file: \"" + profilesOutputFileName  + "\"");
                    System.out.println("events   output file: \"" + eventsOutputFileName   + "\"");
                    System.out.println("error    output file: \"" + errorsOutputFileName   + "\"");
                    System.out.println("---------- summary ----------");
                    System.out.println("ok? [y / n]:");
                    String confirm = (String) shellRdr.readLine();
                    if(confirm.equals("y") || confirm.equals("Y")) { finalOK = true;  break; }
                    if(confirm.equals("n") || confirm.equals("N")) { finalOK = false; break; }
                    if(confirm.equals("q") || confirm.equals("Q")) { System.exit(1); }
                    System.out.println("__ERROR__: invalid input. enter either \"y\" or \"n\""); System.out.println();
                } catch(IOException e) { System.err.println("__ERROR__: I/O exception."); }
            }
        } // ___finalOK = true___
        
        System.out.println();  System.out.println("Main::_main()____starting..... starting..... starting.....");
        
        try {
            Set<String> workQueue    = new HashSet<String>(numbSymbols);
            
            BufferedWriter profileWriter = new BufferedWriter(new FileWriter(new File(profilesOutputFileName)));
            BufferedWriter eventWriter   = new BufferedWriter(new FileWriter(new File(eventsOutputFileName)));
            BufferedWriter errorWriter   = new BufferedWriter(new FileWriter(new File(errorsOutputFileName)));
            
            Main.writeEventFileHeader(eventWriter);
            Main.writeProfileFileHeader(profileWriter);
            errorWriter.write("none of these symbols can be understood by http://finance.yahoo.com");
            errorWriter.flush();
            
            ThreadGroup workerGroup = new ThreadGroup("workers");
            List<Worker> workerList   = new ArrayList<Worker>(numbWorkers);
            for(int i = 0; i < numbWorkers; i++) {
                Worker w = new Worker(eventsStartDateString, eventsEndDateString,
                                        profilesStartDateString, profilesEndDateString,
                                        profileWriter,           eventWriter,   errorWriter,
                                        workQueue,               productType,   Integer.toString(i + 1));
                Thread t = new Thread(workerGroup, w);
                t.setName("worker #" + Integer.toString(i + 1));
                workerList.add(i, (Worker) w);
                t.start();
            }

            BufferedReader symbFileRdr = new BufferedReader(new FileReader(new File(symbInputFileName)));
            String         symb        = symbFileRdr.readLine();
            synchronized(workQueue) {
                while((symb = symbFileRdr.readLine()) != null) {
                    symb = symb.replaceAll("/", "-");
                    workQueue.add(symb);
                }
                workQueue.notify();
            }
            symbFileRdr.close();

            int initialAmountOfWork = workQueue.size();
            int updateGranularity   = (int) (initialAmountOfWork * ((float) .1)); // update every 10% of workQueue complete.
 
         
        
            synchronized(workQueue) {
                while(workerGroup.activeCount() > 0) {
                    workQueue.notify();
                    try { workQueue.wait(1500); } catch (InterruptedException e) { }
                    
                    if(((updateGranularity != 0) && ((workQueue.size() % updateGranularity) == 0)) ||
                            (workQueue.size() < updateGranularity)) {
                        double d         = (double) (((double) workQueue.size()) / ((double) initialAmountOfWork));
                        int    updatePct = (int)    (((double) d) * ((double) 100));
                        String updatePctString  = Integer.toString(updatePct);
                        StringBuilder statusBar = new StringBuilder("[-");
                        for(int i = 0; i < 9; i++) {
                            if(i < (((int) updatePct) / 10)) {
                                statusBar.append("-");
                            } else {
                                statusBar.append(" ");
                            }
                        }
                        statusBar.append(" ]");
                        if(workQueue.size() < updateGranularity) {

                            NumberFormat f = NumberFormat.getInstance();
                            f.setMaximumFractionDigits(4);
//                            f.setMinimumFractionDigits(4);
                            
                            double numerator = workQueue.size();
                            if(numerator == 0) {
                                numerator = workerGroup.activeCount();
                            }
                            double x = (double) (((double) numerator) / ((double) initialAmountOfWork));
                            updatePctString = f.format(x);
                            if(x < 1) {
                                 try { Thread.sleep(2001); } catch(InterruptedException e) { }
                            }
                        }
                        if(Double.valueOf(updatePctString) == 0) {
                            statusBar = new StringBuilder("[ ");
                            for(int i = 0; i < 9; i++) { statusBar.append(" "); }
                            statusBar.append(" ]");
                        }
                        System.out.println("Main::__" + statusBar.toString() + "__remaining work ---> " + updatePctString + "%"); 
                    }
                    
                }
            }
            profileWriter.close();
            eventWriter.close();
            errorWriter.close();
            
            System.out.println();
            System.out.println("---------------------------------------------");
            for(int i = 0; i < workerList.size(); i++) {
                Worker w = (Worker) workerList.get(i);
                System.out.println(w);
            }
            System.out.println("---------------------------------------------");
            System.out.println("errors:   \"" + errorsOutputFileName   + "\"");
            System.out.println("events:   \"" + eventsOutputFileName   + "\"");
            System.out.println("profiles: \"" + profilesOutputFileName + "\"");
            System.out.println();
            System.out.println("Visit here to understand more about input/output files:");
            System.out.println("http://www.dwilkins.net/Dev/DataBot.aspx");
            System.out.println();
            System.out.println("Main::_main()____---exit---exit---exit---exit---____");
            System.out.println();
        } catch(IOException e) { e.printStackTrace(); }
    } 
    
    
    private static boolean validateWritableFile(String fileName) {
        if(fileName.length() == 0) {
            System.err.println("__ERROR__: no default file name provided. something must be entered as an output file.");
            return(false);
        }
        if(!(( fileName.toCharArray()[0] == "/".toCharArray()[0]) ||
                ( fileName.toCharArray()[0] == "\\".toCharArray()[0]))) {
            System.err.println("__ERROR__: the full path name must be used. btw: do not use a drive letter.");
            return(false);
        }
        File f = new File(fileName);
        if(f.exists()) {
            System.err.println("__ERROR__: a file with this name ---> \"" + fileName + "\" already exists.");
            return(false);
        }
        try {
            if(f.createNewFile()) { f.delete(); }
        } catch(IOException e) {
            System.err.println("__ERROR__: file cannot be created. write permission problem? directory does not exist?");
            return(false);
          }
        return(true);
    }    
            
        
    private static int getNumbOfLinesInFile(String fileName) {
        int x = 0;
        try {
            BufferedReader fileRdr = new BufferedReader(new FileReader(new File(fileName)));
            String whatEver = "_null";       
            while((whatEver = fileRdr.readLine()) != null) { x++; }
            fileRdr.close();
        } catch(FileNotFoundException e) { return(-1); }
          catch(IOException           e) { return(-1); }
        return(x);
    }
    
    
    private static boolean validateReadableFile(String fileName) {
        if(fileName.length() == 0) {
            System.err.println("__ERROR__: no default file name provided. something must be entered as an output file.");
            return(false);
        }
        if(!(( fileName.toCharArray()[0] == "/".toCharArray()[0]) ||
                ( fileName.toCharArray()[0] == "\\".toCharArray()[0]))) {
            System.err.println("__ERROR__: the full path name must be used. btw: do not use a drive letter.");
            return(false);
        }
        File f = new File(fileName);
        if(!f.exists()) {
            System.err.println("__ERROR__: file not found. what/where is ---> \"" + fileName + "\"?");
            return(false);
        }
        return(true);
    }
  
    
    private static boolean validateDateFormat(String date) {
        if(!(date.length() == 10)) {
            System.out.println("__ERROR__: invalid date format. example: enter \"Feb. 9, 1997\" as: \"02-09-1997\". must be 10 characters in length.");
            return(false);
        }
        if((date.charAt(2) != ("-".toCharArray()[0]))||(date.charAt(5) != ("-".toCharArray()[0]))) {
            System.out.println("__ERROR__: invalid date format. example: enter \"May. 1, 2003\" as: \"05-01-2003\". must use \"-\" as delimiter.");
            return(false);
        }
        
        StringTokenizer tokenizer = new StringTokenizer(date);
        try {
            String month = tokenizer.nextToken("-");
            int mm = Integer.valueOf(month).intValue();
            if((mm < 1) || (mm > 12)) {
                System.out.println("__ERROR__: the \"month\" must be between 01 [January] and 12 [December]. found = \"" + mm + "\"");
                return(false);
            }
            
            try {
                String day = tokenizer.nextToken("-");
                int dd = Integer.valueOf(day).intValue();
                if(dd < 1) {
                    System.out.println("__ERROR__: the \"day\" must be a positive integer. found = \"" + dd + "\"");
                    return(false);
                }
                switch(mm) {
                    case 1:
                        if(dd > 31) { System.out.println("__ERROR__: January has less than 32 days.");
                        return(false); }
                    case 3:
                        if(dd > 31) { System.out.println("__ERROR__: March has less than 32 days.");
                        return(false); }
                    case 4:
                        if(dd > 30) { System.out.println("__ERROR__: April has less than 31 days.");
                        return(false); }
                    case 5:
                        if(dd > 31) { System.out.println("__ERROR__: May has less than 32 days.");
                        return(false); }
                    case 6:
                        if(dd > 30) { System.out.println("__ERROR__: June has less than 31 days.");
                        return(false); }
                    case 7:
                        if(dd > 31) { System.out.println("__ERROR__: July has less than 32 days.");
                        return(false); }
                    case 8:
                        if(dd > 31) { System.out.println("__ERROR__: August has less than 32 days.");
                        return(false); }
                    case 9:
                        if(dd > 30) { System.out.println("__ERROR__: September has less than 31 days.");
                        return(false); }
                    case 10:
                        if(dd > 31) { System.out.println("__ERROR__: October has less than 32 days.");
                        return(false); }
                    case 11:
                        if(dd > 30) { System.out.println("__ERROR__: November has less than 31 days.");
                        return(false); }
                    case 12:
                        if(dd > 31) { System.out.println("__ERROR__: December has less than 32 todays.");
                        return(false); }
                }
                
                try {
                    String year = tokenizer.nextToken("-");
                    if(year.length() < 4) {
                        System.out.println("__ERROR__: the year cannot be abbreviated. example: for \"12-25-97\" enter --> \"12-25-1997\". found year = \"" + year + "\"");
                        System.out.println(); System.out.println(); return(false);
                    }
                    if(year.length() > 4) {
                        System.out.println("__ERROR__: no data available yet for the next millennium. found year = \"" + year + "\"");
                        System.out.println(); System.out.println(); return(false);
                    }
                    int yyyy = Integer.valueOf(year).intValue();
                    if(yyyy < 1962) {
                        System.out.println("NOTICE: no data is available before 01-01-1962."); System.out.println();
                    }
                    if(mm == 2) {
                        if( ( (yyyy%4) != 0) && (dd == 29) ) {
                            System.out.println("__ERROR__: \"" + yyyy + "\" is not a leap year.");
                            System.out.println(); System.out.println(); return(false);
                        }
                        if ( ( (yyyy%4) == 0) && (dd > 29) ) {
                            System.out.println("__ERROR__: even on a leap year, February still has less than 30 days.");
                            System.out.println(); System.out.println(); return(false);
                        }
                        if ( ( (yyyy%4) != 0) && (dd > 28) ) {
                            System.out.println("__ERROR__: except on leap year, February has less than 29 days.");
                            System.out.println(); System.out.println(); return(false);
                        }
                    }
                } catch(NumberFormatException e) {
                    System.out.println("__ERROR__: the four characters in the year field must all be integers.");
                    System.out.println(); System.out.println(); return(false);
                }
            } catch(NumberFormatException e) {
                System.out.println("__ERROR__: the two characters in the day field must both be integers.");
                System.out.println(); System.out.println(); return(false);
            }
        } catch(NumberFormatException e) {
            System.out.println("__ERROR__: the two characters in the month field must both be integers");
            System.out.println(); System.out.println(); return(false);
        }
        return(true);
    }
    
    private static void writeEventFileHeader(BufferedWriter writer) throws IOException {
        StringBuilder writeThis = new StringBuilder("symb;");
        writeThis.append("date;");
        writeThis.append("open;");
        writeThis.append("high;");
        writeThis.append("low;");
        writeThis.append("close;");
        writeThis.append("volume;");
        writeThis.append("adjClose;");
        writeThis.append("dolVolume;");
        writeThis.append("closeOpenPriceDiff;");
        writeThis.append("highLowPriceDiff;");
        writeThis.append("overnightPriceDiff;");
        writeThis.append("adjCloseDiff;");
        writeThis.append("volumeDiff;");
        writeThis.append("dolVolumeDiff;");
        writer.write(writeThis.toString());
    }
        
    private static void writeProfileFileHeader(BufferedWriter writer) throws IOException {
        StringBuilder writeThis = new StringBuilder("symb;");
        writeThis.append("profileStart;");
        writeThis.append("profileEnd;");
        writeThis.append("adjCls;");
        writeThis.append("adjClsMean;");
        writeThis.append("adjClsMin;");
        writeThis.append("adjClsMax;");
        writeThis.append("adjClsMedian;");
        writeThis.append("adjClsStdDev;");
        writeThis.append("volumeMean;");
        writeThis.append("volumeMin;");
        writeThis.append("volumeMax;");
        writeThis.append("volumeMedian;");
        writeThis.append("volumeStdDev;");
        writeThis.append("dolVolumeMean;");
        writeThis.append("dolVolumeMin;");
        writeThis.append("dolVolumeMax;");
        writeThis.append("dolVolumeMedian;");
        writeThis.append("dolVolumeStdDev;");
        writeThis.append("adjClsDiffMean;");
        writeThis.append("adjClsDiffStdDev;");
        writeThis.append("closeOpenPriceDiffMean;");
        writeThis.append("closeOpenPriceDiffStdDev;");
        writeThis.append("highLowPriceDiffMean;");
        writeThis.append("highLowPriceDiffStdDev;");
        writeThis.append("overnightPriceDiffMean;");
        writeThis.append("overnightPriceDiffStdDev;");
        writeThis.append("volumeDiffMean;");
        writeThis.append("volumeDiffStdDev;");
        writeThis.append("dolVolumeDiffMean;");
        writeThis.append("dolVolumeDiffStdDev;");
        writeThis.append("productType;");
        writer.write(writeThis.toString());
    }
        
}
